﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Interpreter
{
	internal class StackTrace
	{
		public string File { get; set; }
		public string Line { get; set; }
		public string ColumnPointer { get; set; }
		public int LineNumber { get; set; }
		public string Signature { get; set; }
		public StackTrace Next { get; set; }

		public StackTrace(StackContext callStack)
		{
			Metadata metadata = callStack.Metadata;
			int pc = callStack.ProgramCounter;

			int[] ops = metadata.OpCodes;
			Token[] tokenTableEntry = metadata.TokenInfo[pc];
			Token token = tokenTableEntry.Length == 0 ? null : tokenTableEntry[0];
			string file = null;
			string function = null;

			// Find the next stack data marker.
			for (int i = pc; i < ops.Length; ++i)
			{
				if (ops[i] == Interpreter.BC_STACK_DATA)
				{
					file = metadata.StackInfo[metadata.OpFirstParam[i]];
					function = metadata.StackInfo[metadata.OpMultiParam[i][0]];
					break;
				}
			}

			this.File = file;
			if (token == null)
			{
				this.Line = null;
			}
			else
			{
				this.Line = token.Line;
				this.LineNumber = token.LineNum;
				this.ColumnPointer = token.ColumnPointer;
			}
			this.Signature = function;

			if (callStack.Below != null)
			{
				this.Next = new StackTrace(callStack.Below);
			}
		}

		public string Serialize()
		{
			List<string> output = new List<string>();

			StackTrace start = this;
			while (start != null && start.File.StartsWith("$"))
			{
				start = start.Next;
			}

			for (StackTrace walker = start; walker != null; walker = walker.Next)
			{
				output.Add("> " + walker.File + ", line " + walker.LineNumber + ": " + walker.Signature);
				if (walker.Line != null)
				{
					string line = walker.Line;
					string pointer = walker.ColumnPointer;

					string newLine = line.TrimStart();
					int diff = line.Length - newLine.Length;
					if (diff != 0)
					{
						pointer = pointer.Substring(diff);
					}
					
					output.Add("    " + newLine.TrimEnd());
					output.Add("    " + pointer);
				}
			}

			return string.Join("\n", output);
		}
	}
}
